package net.oschina.allchat.server.framework.core.internal.repository;

import java.util.Stack;

import net.oschina.allchat.server.framework.core.IService;
import net.oschina.allchat.server.framework.core.commons.osgi.IBundleContextAware;
import net.oschina.allchat.server.framework.core.config.IApplicationConfiguration;
import net.oschina.allchat.server.framework.core.config.IApplicationConfigurationAware;
import net.oschina.allchat.server.framework.core.config.IConfiguration;
import net.oschina.allchat.server.framework.core.config.IConfigurationAware;
import net.oschina.allchat.server.framework.core.config.IConfigurationManager;
import net.oschina.allchat.server.framework.core.repository.IComponentInfo;
import net.oschina.allchat.server.framework.core.repository.IDependencyInfo;
import net.oschina.allchat.server.framework.core.repository.IInitializable;
import net.oschina.allchat.server.framework.core.repository.IServiceWrapper;
import net.oschina.allchat.server.framework.core.repository.ServiceCreationException;

public class ServiceWrapper implements IServiceWrapper {
	private IApplicationConfiguration appConfiguration;
	private IConfigurationManager configurationManager;
	private IComponentInfo componentInfo;
	
	public ServiceWrapper(IApplicationConfiguration appConfiguration, IConfigurationManager configurationManager,
			IComponentInfo componentInfo) {
		this.appConfiguration = appConfiguration;
		this.configurationManager = configurationManager;
		if (!componentInfo.isService()) {
			throw new IllegalArgumentException(String.format("component(id: %s) should be a service",
					componentInfo.getId()));
		}
		
		this.componentInfo = componentInfo;
	}

	@Override
	public String getId() {
		return componentInfo.getId();
	}

	@Override
	public IService create() throws ServiceCreationException {
		try {
			return (IService)createComponent(componentInfo, new DependencyPath(componentInfo.getId()));
		} catch (Exception e) {
			throw new ServiceCreationException(String.format("can't create service '%s'",
					componentInfo.getId()), e);
		}
	}

	private Object createComponent(IComponentInfo componentInfo, DependencyPath path) throws Exception {
		IConfiguration configuration;
		if (path.depth() == 0) { // service component
			configuration = configurationManager.getConfiguration(
						path.getServiceId());
		} else { // normal component
			configuration = configurationManager.getConfiguration(
					path.getServiceId(), path.toString());
		}
		
		Object component = componentInfo.create();
		for (IDependencyInfo dependency : componentInfo.getDependencies()) {
			for (IComponentInfo bindedIComponentInfo : dependency.getBindedComponents()) {
				path.enter(bindedIComponentInfo.getId());
				Object bindedComponent = createComponent(bindedIComponentInfo, path);
				dependency.injectComponent(component, bindedComponent);
				path.exit();
			}
		}
		
		if (component instanceof IConfigurationAware) {
			((IConfigurationAware)component).setConfiguration(configuration);
		}
		
		if (component instanceof IApplicationConfigurationAware) {
			((IApplicationConfigurationAware)component).setApplicationConfiguration(appConfiguration);
		}
		
		if (component instanceof IBundleContextAware) {
			((IBundleContextAware)component).setBundleContext(componentInfo.getBundleContext());
		}
		
		if (component instanceof IInitializable) {
			((IInitializable)component).init();
		}
		
		return component;
	}
	
	private class DependencyPath {
		private static final String SEPEARATOR_DEPENDENCY_PATH = "_";
		private String serviceId;
		private Stack<String> paths = new Stack<String>();
		
		public DependencyPath(String serviceId) {
			this.serviceId = serviceId;
		}
		
		public String getServiceId() {
			return serviceId;
		}
		
		public void enter(String path) {
			paths.push(path);
		}
		
		public void exit() {
			if (!paths.empty())
				paths.pop();
		}
		
		@Override
		public String toString() {
			if (paths.size() == 0) {
				return null;
			}
			
			StringBuilder sb = new StringBuilder();
			for (String path : paths) {
				sb.append(path).append(SEPEARATOR_DEPENDENCY_PATH);
			}
			sb.deleteCharAt(sb.length() - 1);
			
			return sb.toString();
		}
		
		public int depth() {
			return paths.size();
		}
	}

}
